home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The PC-SIG Library 10
/
The PC-Sig Library - Shareware for the IBM PC and Compatibles (PC-SIG)(Tenth Edition Disks 1-2804)(1991).iso
/
PC_SIGCD
/
02
/
8
/
DISK0285.ZIP
/
DEBUGFIX.TXT
< prev
next >
Wrap
Text File
|
1984-07-11
|
11KB
|
380 lines
File: DEBUGFIX.TXT On: CLUBware Software Diskette #1
Author: Stan Radzio
Copyright (C) 1984. RAYHAWK AUTOMATION, N W, INC.
This patch to DOS 2.0 DEBUG prevents system lockup during trace
operations.
While single stepping through programs with the DEBUG T (trace)
command, a timer interrupt may occur at the moment DEBUG transfers
control to the next program instruction to execute. Instead of single
stepping the next instruction from the user's program, DEBUG begins
execution of the first instruction within the timer interrupt routine:
an STI at location F000:FEA5 .
When this happens, DEBUG displays the current register contents, the
STI instruction from the timer interrupt routine, the DEBUG command
prompt (-), then waits for a command from the keyboard.
Unfortunately, the timer interrupt is an external hardware interrupt
prioritized by the 8259 interrupt controller chip present on the PC
motherboard. Once a timer interrupt occurs, no further external
interrupts are allowed by the 8259 until an end-of-interrupt signal is
sent to the 8259. Characters are transmitted from the keyboard to the
PC by means of an external interrupt. DEBUG is left in a state where
it is waiting for a command from the keyboard while the keyboard
cannot communicate with the PC because external interrupts have been
temporarily disabled by the 8259. The only recovery possible at this
point is to power down the PC, then power up for a cold reboot.
We have seen one previous fix to this problem. The circumvention is
to disable timer interrupts while tracing program execution. From
within DEBUG, use the I (in) command to retrieve the current contents
of port 21h. This is the mask used by the 8259 to determine which
external interrupts will be passed through to the 8088 CPU.
-I 21
BC
-
The normal mask is BC allowing keyboard interrupts, disk controller
interrupts, and timer interrupts. Change the mask to BD to prevent
timer interrupts.
-O 21 BD
-
There is a major problem with this process other than inaccuracies in
the time of day. The timer interrupt service routine is used by the
BIOS to turn off the diskette drive motor after diskette operations.
With the timer interrupts masked off, the next diskette I/O will start
the motor and the motor will remain on indefinitely.
A true fix for this problem must be a modification to DEBUG itself.
Once the problem occurs, the keyboard is silenced and there is no
opportunity to invoke special service routines to bail us out.
File: DEBUGFIX.TXT Page 2
One modification to DEBUG would be to have DEBUG send an
end-of-interrupt signal to the 8259 just before requesting the next
command from the keyboard. However, the BIOS routines have
initialized the 8259 to recognize a non-specific end-of-interrupt.
The end-of-interrupt signal is a 20h sent to port 20h and means an end
to the current external interrupt whatever it may be. The possibility
of DEBUG clearing an external interrupt not related to the timer is
very small with the current hardware and software available for the
PC. As for the future...?
Without access to the source code for DEBUG, we have developed a
modification to DEBUG which eliminates the problem and is completely
safe. Our modification is to compare the address of the interrupted
routine to the list of hardware interrupt service routines in low
memory. If the address matches, we know that we collided with the
interrupt and must act quickly to prevent the system from becoming
deaf and mute.
When the timer interrupts the code being traced, DEBUG is given
control with the stack in this state:
SP FEA5 - offset of timer interrupt routine
SP+2 F000 - segment of timer interrupt routine
SP+4 flags - saved copy of timer's flags
(trap flag bit is set)
SP+6 XXXX - offset of user code being traced
SP+8 ZZZZ - segment of user code being traced
SP+10 flags - saved copy of user's flags
(trap flag bit is set)
Our modification looks at the stack to see the F000:FEA5 and
recognizes we are in trouble. To extricate ourselves, we clear the
trap flag bit from SP+4, the timer's flags, and perform an IRET to
return control to the timer interrupt routine. The timer code updates
his count of the seconds in the day, turns the diskette motor off if
needed, signals an end-of-interrupt to the 8259 (very important to
us), then performs an IRET which transfers control to our code at
SP+6. The trap flag at SP+10 is still set so a single step interrupt
again occurs and DEBUG trace picks up normally as if nothing strange
had happened. A similar process occurs when any external hardware
interrupt other than the timer occurs during the trace command.
The code we wish to add to DEBUG should be invoked whenever a single
step interrupt occurs. Locating the correct insertion point within
DEBUG.COM is as simple as looking backwards from the interrupt vector
stored at 0000:0004, the single step interrupt vector. This interrupt
vector is 0600:08ED for my version of DOS. The segment value, 600,
varies with the number of I/O buffers, whether there are other
interrupt service routines resident below DEBUG, whether we are
executing from within a BAT file or invoked directly from DOS, etc,
etc. The important piece of information obtained from this vector is
8ED, the offset relative to the start of DEBUG where we want to begin
our code changes.
File: DEBUGFIX.TXT Page 3
Within DEBUG at this offset is code to swap the stack and save all the
registers.
600:8ED MOV WORD PTR CS:[2AEA],SP
600:8F2 MOV WORD PTR CS:[XXXX],SS
We will replace the MOV instruction at 8ED with a JMP to our special
code. When our code completes, we will execute the MOV instruction we
have displaced and JMP back to 600:8F2.
We need to find a safe place in memory to insert 35 instructions
within DEBUG.COM. This is not easy without the source code. We have
chosen to insert the extra code into the location currently occupied
by the user's program segment prefix. In turn, the program segment
prefix is pushed up in memory by 64 bytes. The prefix must be on a
paragraph boundary so the number of bytes stolen must be a multiple of
16.
The next logical step is to begin looking around inside DEBUG for
an invocation of the DOS function 26h which builds the user's program
segment prefix. The DEBUG code at 600:138 asks DOS to build a program
segment prefix at 600:2f20 but when the smoke clears and the user's
program actually starts execution, the prefix has been moved to
600:2f60 and the area at 2f20 has been trashed. Since we don't have
DEBUG source, our only possible work around is to pad this area,
soaking up 144 bytes to insert a patch less than 64 bytes long.
We modify DEBUG code at 600:138 from
MOV AX,2F17
to
MOV AX,2FA0
For good measure we use the DEBUG S (search) command to look around
inside DEBUG.COM for any other occurrences of the string 2F17. This
search reveals a second place within DEBUG which must be changed, this
time at 600:183 from
MOV BX,2F17
to
MOV BX,2FA0
The remaining modifications are straight-forward. Modify the
instruction pointed to by the single step interrupt vector (600:8ED)
from
MOV WORD PTR CS:[2AEA],SP
to
JMP 2F60
File: DEBUGFIX.TXT Page 4
At 600:2F60 start adding our special instructions
PUSH BP
MOV BP,SP
PUSH AX
PUSH BX
PUSH CX
PUSH DI
PUSH ES
SUB AX,AX
MOV ES,AX
MOV DI,20
MOV AX,WORD PTR [BP+2]
MOV BX,WORD PTR [BP+4]
MOV CX,8
SCASW
JNE 2F8C
ES:CMP BX,WORD PTR [DI]
JNE 2F8C
AND WORD PTR [BP+6],FEFF
POP ES
POP DI
POP CX
POP BX
POP AX
POP BP
IRET
If the PUSH BP is at 2F60 then the following ADD is at 2F8C.
ADD DI,2
LOOP 2F78
POP ES
POP DI
POP CX
POP BX
POP AX
POP BP
CS:
MOV [2AEA],SP
JMP 8F2
We set BP to point to the stack. Since BP itself is now on the stack,
the offsets listed earlier relative to SP are two bytes off. We check
the segment and offset stored on the stack to see if it is an external
interrupt service routine. If it isn't, we restore the registers,
execute the instruction which used to be at 600:8ED, and JMP back to
600:8F2. If it is an external interrupt, we clear the trap flag with
the AND instruction, restore the registers, then IRET back to the
interrupt service routine.
File: DEBUGFIX.TXT Page 5
As distributed, the DEBUG.COM file does not contain the temporary
variables used by DEBUG during execution. These variables reside
between 600:2E80 (end of the DEBUG code) and 600:2F60 (eventual home
of the user's program segment prefix). Since we are storing our code
at 2F60, we must enlarge the DEBUG.COM file to include our added code
and the intervening temp space.
We modify DEBUG.COM using the DEBUG commands
-n debug.com
-l
make the changes and then enlarge DEBUG.COM by writing back to disk
more than we read
-r cx
CX 2E80
:2FA0
-w
Writing 2FA0 bytes
The simplest means of propagating this fix to other interested users
is to create a text file containing all the needed DEBUG commands,
then use the TYPE command to put the text file into the pipeline and
feed the commands to DEBUG itself. If we put the following commands
in a file named DEBUG20.FIX
n debug.com
l
a 138
mov ax,2fa0
a 183
mov bx,2fa0
a 8ed
jmp 2f60
a 2f60
push bp
mov bp,sp
push ax
push bx
push cx
push di
push es
sub ax,ax
mov es,ax
mov di,20
mov ax,word ptr [bp+2]
mov bx,word ptr [bp+4]
mov cx,8
scasw
--------------- Code split for page separation. No blank lines here
File: DEBUGFIX.TXT Page 6
--------------- Code split for page separation. No blank lines here
jne 2f8c
es:cmp bx,word ptr [di]
jne 2f8c
and word ptr [bp+6],feff
pop es
pop di
pop cx
pop bx
pop ax
pop bp
iret
add di,2
loop 2f78
pop es
pop di
pop cx
pop bx
pop ax
pop bp
cs:mov [2aea],sp
jmp 08f2
r cx
2fa0
w
q
Then with a copy of DEBUG.COM on the diskette in the default drive
merely give the following pipelined command to DOS.
TYPE DEBUG20.FIX | DEBUG
The blank lines within the DEBUG20.FIX file are needed for correct
execution of the commands. Once all the commands have executed, your
DEBUG patch is complete.
The cost of the patch is a DEBUG.COM file which requires 288 more
bytes to store on diskette and which consumes an extra 144 bytes of
memory when executed.